/***************************************************************************
 *   Copyright (C) 2008 by Marco Gulino   *
 *   marco.gulino@gmail.com   *
 *                                                                         *
 *   This program is free software; you can redistribute it and/or modify  *
 *   it under the terms of the GNU General Public License as published by  *
 *   the Free Software Foundation; either version 2 of the License, or     *
 *   (at your option) any later version.                                   *
 *                                                                         *
 *   This program is distributed in the hope that it will be useful,       *
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
 *   GNU General Public License for more details.                          *
 *                                                                         *
 *   You should have received a copy of the GNU General Public License     *
 *   along with this program; if not, write to the                         *
 *   Free Software Foundation, Inc.,                                       *
 *   51 Franklin Street, Fifth Floor Boston, MA 02110-1301, USA            *
 ***************************************************************************/
#include "checksummer.h"
#include "qfileinfointerface.h"
#include "collectionutils.h"
#include <QDataStream>
#include <QFSFileEngine>

#include <QDebug>
#define KB 1024
#define MB KB*1024

CheckSummer::CheckSummer(QCryptographicHash::Algorithm algo, QObject *parent)
 : QObject(parent), checkSummer(algo)
{
    can_mmap=QFSFileEngine().supportsExtension(QAbstractFileEngine::MapExtension);
    if(!can_mmap) {
        qDebug() << "Warning: mmap not supported, using slow QDataStream copy for checksum" << endl;
    }
}


CheckSummer::~CheckSummer()
{
}

void CheckSummer::checkSumFile(QFileInfoInterface *fileInfo)
{
    checkSummer.reset();
    QFile file(fileInfo->filePath());
    if(!file.open(QIODevice::ReadOnly) ) return;
    QByteArray checksum;
    if(can_mmap) {
        checksum=checkSumFile_MMAP(&file, fileInfo->size() );
    }
    else {
        checksum=checkSumFile_DataStream(&file);
    }
    file.close();
    fileInfo->setChecksum(checksum);
}

const QByteArray CheckSummer::checkSumFile_DataStream(QFile *file)
{
    QDataStream stream(file);
    long chunkSize=500;
    char *buffer=new char[chunkSize];
    while( ! stream.atEnd() ) {
        long readData=stream.readRawData(buffer, chunkSize);
        checkSummer.addData(buffer, readData);
    }
    delete [] buffer;
    return checkSummer.result();
}



const QByteArray CheckSummer::checkSumFile_MMAP(QFile *file, qint64 size)
{
    long chunkSize=50*MB;
    uchar* buffer;
    qint64 mappedSize=0;
    for( qint64 pos=0; pos<size; pos+=chunkSize) {
        mappedSize = (pos+chunkSize)>size?(size-pos):chunkSize;

	QString filePos=file->fileName();
        buffer=file->map(pos, mappedSize);
        if(buffer==NULL) {
            perror( QString("mmap error on file byte %1").arg(pos).toLatin1().data() );
        }
        checkSummer.addData( (char*) buffer, mappedSize);
        file->unmap(buffer);

    }
    return checkSummer.result();
}


QFileInfoChecksummedHash &CheckSummer::hashChecksummed(const QFileInfoInterfaceHash &files)
{
    checksummed.clear();
    foreach(QFileInfoInterface *file, files) {
        checkSumFile(file);
        checksummed.insert(file->checksum(), file);
    }
    CollectionUtils::deleteUniqueValues(&checksummed);
    return checksummed;
}
